home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 04 - 1988 / 04.10 Oct 88 / TransferSources / drvr.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-05-22  |  19.2 KB  |  751 lines  |  [TEXT/MPS ]

  1. (*******************************************************************
  2.     
  3.     drvr.pas
  4.     --------
  5.     
  6.     (c) 1987, 1988 Attic Software
  7.     
  8.     Pascal routines for DRVR segment of Transfer
  9.     
  10. *******************************************************************)
  11.  
  12. unit drvr;
  13.  
  14. (******************************************************************)
  15.  
  16.     interface
  17.     
  18. (******************************************************************)
  19.  
  20.     uses macintf, hfs, types;
  21.     
  22. (******************************************************************)
  23.  
  24.     implementation
  25.     
  26. (******************************************************************)
  27.  
  28.     procedure initglobals(globals : wpointer); external;
  29.     function setdir(dirid : long; globals : wpointer) : OSErr; external;
  30.     
  31. (*******************************************************************
  32.     
  33.     procedure drawscreen
  34.     --------------------
  35.     
  36.     This routine draws the “About” message in its rectangle.
  37.     
  38. *******************************************************************)
  39.  
  40.     procedure drawscreen(theWindow : WindowPtr; theitem : integer);
  41.     
  42.         var
  43.             theType            :    integer;
  44.             theHandle        :    Handle;
  45.             thebox            :    Rect;
  46.         
  47.         begin
  48.             
  49.             SetPort(theWindow);
  50.             
  51.             GetFNum('monaco', theType);
  52.             TextFont(theType);
  53.             TextSize(9);
  54.             
  55.             GetDItem(theWindow, theitem, theType, theHandle, thebox);
  56.             
  57.             theHandle := GetResource('INFO', GetWRefCon(theWindow));
  58.             HLock(theHandle);
  59.             TextBox(theHandle^, GetHandleSize(theHandle), thebox, 0);
  60.             HUnlock(theHandle);
  61.             
  62.         end;
  63.         
  64. (*******************************************************************
  65.     
  66.     procedure transalert
  67.     --------------------
  68.     
  69.     I want all the alerts to identify their source, so I made them
  70.     dialogs, in titled windows, and wrote this routine to imitate
  71.     “Alert”.
  72.     
  73.     This is not the best way to do it; for one thing, a titled window
  74.     should be dragable.  Modal dialogs, therefore, should not have
  75.     titles.  I should have simply added a distinctive icon to the
  76.     item lists.
  77.     
  78.     Since this is the first use of the “resfactor” field of the
  79.     global record, it's as good a place as any to explain it.  This
  80.     DA is given a formal resource id of 16, and all its owned resources
  81.     are numbered accordingly.  The Font/DA Mover will change all
  82.     these numbers when it installs the DA.  “resfactor”, which is
  83.     computed in the “open” routine, below, is the correction factor
  84.     that converts the hard-coded resource ids to the actual ids in
  85.     use.
  86.     
  87.     Note that “resfactor” doesn't convert the formal ids; it actually
  88.     converts the constants defined in the “types.pas” unit, which are
  89.     nice positive numbers, equal to the resource ids plus 16872.
  90.     
  91.     There's a little bit of code to install the previous procedure
  92.     if this is the “About” dialog.
  93.     
  94. *******************************************************************)
  95.  
  96.     procedure transalert(dialognum : integer; globals : wpointer);
  97.     
  98.         var
  99.             savedport        :    GrafPtr;
  100.             theDialog        :    DialogPtr;
  101.             therecord        :    DialogRecord;
  102.             theType            :    integer;
  103.             theHandle        :    Handle;
  104.             thebox            :    Rect;
  105.             choice            :    integer;
  106.         
  107.         begin
  108.             
  109.             with globals^ do begin
  110.                 
  111.                 GetPort(savedport);
  112.                 
  113.                 theDialog := GetNewDialog(dialognum + resfactor,
  114.                                 @therecord, pointer(-1));
  115.                 SetPort(theDialog);
  116.                 
  117.                 if dialognum = aboutdialog then begin
  118.                     GetDItem(theDialog, 2, theType, theHandle, thebox);
  119.                     SetDItem(theDialog, 2, theType, Handle(@drawscreen), thebox);
  120.                     SetWRefCon(theDialog, dialognum + resfactor);
  121.                 end;
  122.                     
  123.                 InitCursor;
  124.                 ShowWindow(theDialog);
  125.             
  126.                 repeat
  127.                     ModalDialog(nil, choice);
  128.                 until choice = ok;
  129.                     
  130.                 CloseDialog(theDialog);
  131.                 SetPort(savedport);
  132.             
  133.             end;
  134.             
  135.         end;
  136.     
  137. (*******************************************************************
  138.     
  139.     procedure errordisplay
  140.     ----------------------
  141.     
  142.     This routine displays error messages.  The texts of the messages
  143.     are in a string list.
  144.     
  145. *******************************************************************)
  146.  
  147.     procedure errordisplay(appnum, sysnum, resnum : long;
  148.                     globals : wpointer);
  149.     
  150.         var
  151.             string1            :    Str255;
  152.             string3            :    Str255;
  153.             string4            :    Str255;
  154.     
  155.         begin
  156.         
  157.             InitCursor;
  158.             
  159.             with globals^ do begin
  160.             
  161.                 GetIndString(string1, resfactor + stringnum, resnum);
  162.                 if string1 = '' then
  163.                     string1 := 'An error has occurred!';
  164.             
  165.                 NumToString(appnum, string3);
  166.                 NumToString(sysnum, string4);
  167.                 ParamText(string1, '', string3, string4);
  168.                 
  169.                 SysBeep(10);
  170.                 transalert(errordialog, globals);
  171.             
  172.             end;
  173.         
  174.         end;
  175.     
  176. (*******************************************************************
  177.     
  178.     procedure getapps
  179.     -----------------
  180.     
  181.     Here it is - the only recursive routine I have ever been forced
  182.     to use!  (Recursion is overrated; it is, in my opinion, better
  183.     to avoid recursion, if you can do it in a natural fashion.  Loops
  184.     are easier to read, and generally more efficient.)
  185.     
  186.     This routine is passed a directory id, the count of objects (files
  187.     and folders) in that directory, and the volume reference number.
  188.     It indexes through the directory with “PBGetCatInfo”.
  189.     
  190.     If the object is another directory, it calls itself with that
  191.     directory's id and count.
  192.     
  193.     If the object is a file, and if the file is an application, it
  194.     records its name and the directory id in the application array.
  195.     
  196. *******************************************************************)
  197.         
  198.     procedure getapps(thedir : long; thecount : integer;
  199.                     thevol : integer; theHandle : arrayhandle;
  200.                     globals : wpointer);
  201.         
  202.         label
  203.             100;
  204.         
  205.         var
  206.             index                :    integer;
  207.             anerror            :    integer;
  208.             theDialog        :    DialogPtr;
  209.             jndex                :    integer;
  210.             thelength        :    integer;
  211.             thedesk            :    deskhandle;
  212.             
  213.         begin
  214.             
  215.             with globals^ do begin
  216.         
  217.                 for index := 1 to thecount do with infoblock do begin
  218.                 
  219.                     thename := '';
  220.                 
  221.                     ioCompletion := nil;
  222.                     ioNamePtr := @thename;
  223.                     ioVRefNum := thevol;
  224.                     ioFDirIndex := index;
  225.                     ioDrDirID := thedir;
  226.                 
  227.                     anerror := PBGetCatInfo(@infoblock, false);
  228.                     if anerror <> noErr then begin
  229.                         errordisplay(101, anerror, 2, globals);
  230.                         goto 100;
  231.                     end;
  232.                 
  233.                     if BitAnd(ioFlAttrib, $10) = $10 then
  234.                         getapps(ioDrDirID, ioDrNmFls, thevol, theHandle, globals)
  235.                     else if ioFlFndrInfo.fdType = 'APPL' then begin
  236.                     
  237.                         jndex := theHandle^^.count + 1;
  238.                         theHandle^^.count := jndex;
  239.                         SetHandleSize(Handle(theHandle), 5 * (jndex + 1));
  240.                     
  241.                         HLock(Handle(theHandle));
  242.                     
  243.                         with theHandle^^ do
  244.                             while (IUCompString(thename, data[jndex - 1]^^.name) < 0)
  245.                                             and (jndex > 1) do begin
  246.                                 data[jndex] := data[jndex - 1];
  247.                                 jndex := jndex - 1;
  248.                             end;
  249.                     
  250.                         thelength := 10 + length(thename);
  251.                         thelength := 2 * (thelength div 2);
  252.                         theHandle^^.data[jndex] := deskhandle(NewHandle(thelength));
  253.                         with theHandle^^.data[jndex]^^ do begin
  254.                             dirid := thedir;
  255.                             name := thename;
  256.                         end;
  257.                     
  258.                         HUnlock(Handle(theHandle));
  259.                     
  260.                     end;
  261.             
  262.         100:    end;
  263.             
  264.             end;
  265.  
  266.         end;
  267.         
  268. (*******************************************************************
  269.     
  270.     procedure walktree
  271.     ------------------
  272.     
  273.     This routine catalogs all the applications on a given disk.
  274.     
  275.     It first puts up a dialog, telling the user what's going on.
  276.     
  277.     It next calls “PBGetCatInfo” for the root directory (directory
  278.     id = 2), to get the number of objects in the root.  Then it
  279.     calls “getapps” to walk the HFS tree recursively.
  280.     
  281.     The collected data is written to the current resource file
  282.     (“Transfer Data”, in the System folder), and the dialog is
  283.     dismissed.
  284.     
  285. *******************************************************************)
  286.         
  287.     procedure walktree(thevol : integer; theHandle : arrayhandle;
  288.                     globals : wpointer);
  289.         
  290.         var
  291.             savedport        :    GrafPtr;
  292.             theDialog        :    DialogPtr;
  293.             therecord        :    DialogRecord;
  294.             index                :    integer;
  295.             anerror            :    integer;
  296.             
  297.         begin
  298.             
  299.             with globals^ do begin
  300.                 
  301.                 GetPort(savedport);
  302.                 
  303.                 theDialog := GetNewDialog(resfactor + builddialog,
  304.                                 @therecord, pointer(-1));
  305.                 SetPort(theDialog);
  306.                 ShowWindow(theDialog);
  307.                 DrawDialog(theDialog);
  308.             
  309.                 with infoblock do begin
  310.                     ioCompletion := nil;
  311.                     ioNamePtr := nil;
  312.                     ioVRefNum := thevol;
  313.                     ioFDirIndex := 0;
  314.                     ioDrDirID := 2;
  315.                 end;
  316.                 anerror := PBGetCatInfo(@infoblock, false);
  317.                 if anerror <> noErr then
  318.                     errordisplay(102, anerror, 2, globals)
  319.                 else
  320.                     getapps(2, infoblock.ioDrNmFls, thevol, theHandle, globals);
  321.             
  322.             end;
  323.                 
  324.             HLock(Handle(theHandle));
  325.             with theHandle^^ do
  326.                 for index := 1 to count do begin
  327.                     AddResource(Handle(data[index]), '.Trn',
  328.                                     datastart + index, data[index]^^.name);
  329.                     SetHandleSize(Handle(data[index]), 4);
  330.                 end;
  331.             HUnlock(Handle(theHandle));
  332.                 
  333.             CloseDialog(theDialog);
  334.             SetPort(savedport);
  335.  
  336.         end;
  337.         
  338. (*******************************************************************
  339.     
  340.     procedure buildmenu
  341.     -------------------
  342.     
  343.     This routine assembles the necessary data, and builds the
  344.     Transfer menu.
  345.     
  346.     It finds the volume reference number of the disk with the System
  347.     folder, sets the directory to the System folder, and opens or
  348.     creates the “Transfer Data” file in that directory.
  349.     
  350.     If this file lacks a header resource (whether because it was
  351.     just created, or because it has been corrupted), then it must
  352.     be rebuilt, with “walktree”.
  353.     
  354.     Then the menu is built.  The menu resource is loaded, and the
  355.     fourth item set to the name of the current Finder.  The remainder
  356.     of the menu is copied from the resource file.
  357.     
  358. *******************************************************************)
  359.         
  360.     procedure buildmenu(globals : wpointer);
  361.         
  362.         label
  363.             100;
  364.         
  365.         var
  366.             thepointer        :    shortpointer;
  367.             thevolume        :    integer;
  368.             theres            :    integer;
  369.             theHandle        :    arrayhandle;
  370.             index                :    integer;
  371.             jndex                :    integer;
  372.             thedesk            :    Handle;
  373.             theID                :    integer;
  374.             theType            :    ResType;
  375.             anerror            :    integer;
  376.             
  377.         begin
  378.             
  379.             with globals^ do begin
  380.                 
  381.                 thepointer := shortpointer(sysmap);
  382.                 anerror := GetVRefNum(thepointer^, thevolume);
  383.                 if anerror <> noErr then begin
  384.                     errordisplay(103, anerror, 3, globals);
  385.                     goto 100;
  386.                 end;
  387.             
  388.                 anerror := setdir(sysdir, globals);
  389.                 if anerror <> noErr then begin
  390.                     errordisplay(104, anerror, 3, globals);
  391.                     goto 100;
  392.                 end;
  393.             
  394.                 thename := 'Transfer Data';
  395.                 theres := OpenResFile(thename);
  396.                 if ResError = fnfErr then begin
  397.                     CreateResFile(thename);
  398.                     theres := OpenResFile(thename);
  399.                 end;
  400.                 if ResError <> noErr then begin
  401.                     errordisplay(105, ResError, 3, globals);
  402.                     goto 100;
  403.                 end;
  404.             
  405.                 theHandle := arrayhandle(Get1Resource('.Trn', datastart));
  406.                 if theHandle = nil then begin
  407.                     theHandle := arrayhandle(NewHandle(6));
  408.                     theHandle^^.count := 0;
  409.                     theHandle^^.data[0] := deskhandle(NewHandle(16));
  410.                     AddResource(Handle(theHandle), '.Trn', datastart, '');
  411.                     walktree(thevolume, theHandle, globals);
  412.                     SetHandleSize(Handle(theHandle), 2);
  413.                 end;
  414.             
  415.                 theMenu := GetMenu(resfactor + menunum);
  416.                 BlockMove(Ptr(findername), Ptr(@thename), 16);
  417.                 SetItem(theMenu, 4, thename);
  418.                 
  419.                 jndex := 1;
  420.                 for index := 1 to theHandle^^.count do begin
  421.                     thedesk := Get1Resource('.Trn', datastart + index);
  422.                     if thedesk <> nil then begin
  423.                         AppendMenu(theMenu, '.Trn');
  424.                         GetResInfo(thedesk, theID, theType, thename);
  425.                         SetItem(theMenu, jndex + 4, thename);
  426.                         jndex := jndex + 1;
  427.                     end;
  428.                 end;
  429.             
  430.                 InsertMenu(theMenu, 0);
  431.                 DrawMenuBar;
  432.             
  433.                 CloseResFile(theres);
  434.             
  435.     100:    end;
  436.             
  437.         end;
  438.         
  439. (*******************************************************************
  440.     
  441.     function systemvol
  442.     ------------------
  443.     
  444.     This routine is more or less straight out of Tech Note 77, pages
  445.     3 and 4.  It returns a working directory reference number for the
  446.     System folder, suitable for use in file system calls.
  447.     
  448.     Step one is to find the volume reference number of the volume
  449.     that holds the System folder.  “sysmap” is the file reference
  450.     number of the System file (an open file), so “GetVRefNum” will
  451.     find the volume refence number of the System file and, of course,
  452.     the System folder.
  453.     
  454.     Step two is to get the directory id, with a call to “PBHGetVInfo”.
  455.     The directory id is returned in the “ioVFndrInfo[1]” field of the
  456.     HParamBlockRec.
  457.     
  458.     Finally, “PBOpenWD” will return the System folder's working
  459.     directory reference number, which can be used as a volume
  460.     reference number in file system calls.
  461.     
  462. *******************************************************************)
  463.  
  464.     function systemvol(globals : wpointer) : integer;
  465.         
  466.         var
  467.             thepointer        :    shortpointer;
  468.             thevolume        :    integer;
  469.             anerror            :    integer;
  470.         
  471.         begin
  472.             
  473.             with globals^ do begin
  474.                 
  475.                 thepointer := shortpointer(sysmap);
  476.                 anerror := GetVRefNum(thepointer^, thevolume);
  477.                 
  478.                 with hblock do begin
  479.                     ioNamePtr := nil;
  480.                     ioVRefNum := thevolume;
  481.                     ioVolIndex := 0;
  482.                 end;
  483.                 anerror := PBHGetVInfo(@hblock, false);
  484.                 
  485.                 with wdblock do begin
  486.                     ioWDDirID := hblock.ioVFndrInfo[1];
  487.                     ioNamePtr := nil;
  488.                     ioVRefNum := thevolume;
  489.                     ioWDProcID := erik;
  490.                 end;
  491.                 anerror := PBOpenWD(@wdblock, false);
  492.                 
  493.                 systemvol := wdblock.ioVRefNum;
  494.                 
  495.             end;
  496.         
  497.         end;
  498.         
  499. (*******************************************************************
  500.     
  501.     procedure rebuildmenu
  502.     ---------------------
  503.     
  504.     If the “Rebuild menu” item is chosen from the menu, or an
  505.     application is chosen which can't be found, Transfer will rebuild
  506.     the menu from scratch.  It does this by deleting the “Transfer
  507.     Data” file, and calling “buildmenu”.
  508.     
  509. *******************************************************************)
  510.  
  511.     procedure rebuildmenu(globals : wpointer);
  512.         
  513.         var
  514.             anerror            :    integer;
  515.         
  516.         begin
  517.             
  518.             with globals^ do begin
  519.             
  520.                 DeleteMenu(resfactor + menunum);
  521.                 ReleaseResource(Handle(theMenu));
  522.                 anerror := FSDelete('Transfer Data', systemvol(globals));
  523.                 buildmenu(globals);
  524.             
  525.             end;
  526.             
  527.         end;
  528.         
  529. (*******************************************************************
  530.     
  531.     procedure dofinder
  532.     ------------------
  533.     
  534.     If the “Finder” item is chosen from the menu, then no transfer
  535.     is desired, so restore the “iaznotify” hook to the value it held
  536.     when Transfer was launched. (This isn't quite right, since
  537.     something besides Transfer may have changed it since then.  but I
  538.     don't see any way to correct for that...)
  539.     
  540.     If the option key is down, do nothing else.  Otherwise, do an
  541.     immediate transfer by calling “ExitToShell”.
  542.     
  543. *******************************************************************)
  544.  
  545.     procedure dofinder(globals : wpointer);
  546.         
  547.         var
  548.             thepointer        :    longpointer;
  549.         
  550.         begin
  551.             
  552.             with globals^ do begin
  553.                 
  554.                 thepointer := longpointer(iaznotify);
  555.                 thepointer^ := iazaddr;
  556.                 
  557.                 if GetNextEvent(0, theEvent) then
  558.                     ;
  559.                 if BitAnd(theEvent.modifiers, optionKey) = 0 then
  560.                     ExitToShell;
  561.                 
  562.             end;
  563.             
  564.         end;
  565.         
  566. (*******************************************************************
  567.     
  568.     procedure clickmenu
  569.     -------------------
  570.     
  571.     The first few menu choices are handled by routines above.
  572.     
  573.     If an application is chosen, we need to get (1) the application's
  574.     name, and (2) its directory.  The name is easy; it's on the menu.
  575.     To get the directory, we have to go back to the “Transfer Data”
  576.     file.
  577.     
  578.     Once we have the application's directory, the next thing to do
  579.     is to make sure it's there.  Transferring to a non-existent
  580.     application will cause a system bomb.
  581.     
  582.     If everything is ok, then if the option key is down, prepare to
  583.     do a delayed transfer; otherwise, do an immediate transfer by
  584.     calling “ExitToShell”.
  585.     
  586. *******************************************************************)
  587.  
  588.     procedure clickmenu(itemchoice : integer; globals : wpointer);
  589.         
  590.         label
  591.             100;
  592.         
  593.         var
  594.             theres            :    integer;
  595.             thedesk            :    deskhandle;
  596.             theinfo            :    FInfo;
  597.             thepointer        :    longpointer;
  598.             anerror            :    integer;
  599.         
  600.         begin
  601.             
  602.             with globals^ do begin
  603.             
  604.                 case itemchoice of
  605.                     aboutitem    :    transalert(aboutdialog, globals);
  606.                     builditem    :    rebuildmenu(globals);
  607.                     finderitem    :    dofinder(globals);
  608.                 otherwise
  609.                     
  610.                     thepointer := longpointer(iaznotify);
  611.                     thepointer^ := launchaddr;
  612.                     
  613.                     anerror := setdir(sysdir, globals);
  614.                     if anerror <> noErr then begin
  615.                         errordisplay(106, anerror, 3, globals);
  616.                         goto 100;
  617.                     end;
  618.                 
  619.                     theres := OpenResFile('Transfer Data');
  620.                     if ResError <> noErr then begin
  621.                         rebuildmenu(globals);
  622.                         goto 100;
  623.                     end;
  624.                     
  625.                     GetItem(theMenu, itemchoice, thename);
  626.                     thedesk := deskhandle(Get1NamedResource('.Trn', thename));
  627.                     DetachResource(Handle(thedesk));
  628.                     CloseResFile(theres);
  629.                     
  630.                     if thedesk = nil then begin
  631.                         rebuildmenu(globals);
  632.                         goto 100;
  633.                     end;
  634.                     
  635.                     anerror := setdir(thedesk^^.dirid, globals);
  636.                     if anerror <> noErr then begin
  637.                         rebuildmenu(globals);
  638.                         goto 100;
  639.                     end;
  640.                         
  641.                     anerror := GetFInfo(thename, 0, theinfo);
  642.                     if anerror <> noErr then begin
  643.                         rebuildmenu(globals);
  644.                         goto 100;
  645.                     end;
  646.                     
  647.                     launchpath := thedesk^^.dirid;
  648.                     
  649.                     if GetNextEvent(0, theEvent) then
  650.                         ;
  651.                     if BitAnd(theEvent.modifiers, optionKey) = 0 then
  652.                         ExitToShell
  653.                     else begin
  654.                         ParamText(thename, '', '', '');
  655.                         transalert(delaydialog, globals);
  656.                     end;
  657.                 
  658.                 end;
  659.             
  660.         100:    HiliteMenu(0);
  661.             
  662.             end;
  663.             
  664.         end;
  665.         
  666. (*******************************************************************
  667.     
  668.     procedure open
  669.     --------------
  670.     
  671.     This is the canonical DA open routine.  If the DA has already
  672.     been opened, device.dCtlMenu will be nonzero; do nothing.
  673.     Otherwise, Allocate the globals, and fill in a few fields.  Of
  674.     particular interest is the calculation of “resfactor” by the
  675.     magic formula $BFE0 - 32 * dCtlRefNum - 1000.  This, assuming I
  676.     have given the DA the formal resource id of 16, allows be to
  677.     refer to owned resources by ids from 1000 to 1031, adding
  678.     resfactor to convert to the actual values.
  679.     
  680.     Next, load and detach the PACK segment.  And lock it; it's
  681.     created locked but why take chances?
  682.     
  683.     Finally, call “initglobals” to fill in the rest of the fields,
  684.     and “buildmenu” to set up the menu.
  685.     
  686. *******************************************************************)
  687.         
  688.     procedure open(var device : DCtlEntry; var block : ParamBlockRec);
  689.         
  690.         var
  691.             globals            :    wpointer;
  692.             packhandle        :    Handle;
  693.         
  694.         begin
  695.         
  696.             if device.dCtlMenu = 0 then begin
  697.             
  698.                 globals := wpointer(NewPtr(sizeof(wrecord)));
  699.                 if globals <> nil then with globals^ do begin
  700.                     
  701.                     with device do begin
  702.                         resfactor := $BFE0 - 32 * dCtlRefNum - 1000;
  703.                         dCtlMenu := resfactor + menunum;
  704.                         dctlwindow := nil;
  705.                         dCtlStorage := Handle(globals);
  706.                     end;
  707.                     
  708.                     packhandle := GetResource('PACK', resfactor + packnum);
  709.                     DetachResource(packhandle);
  710.                     HLock(packhandle);
  711.                     packaddr := packhandle^;
  712.             
  713.                     initglobals(globals);
  714.                     buildmenu(globals);
  715.                 
  716.                 end;
  717.             
  718.             end;
  719.         
  720.         end;
  721.     
  722. (*******************************************************************
  723.     
  724.     procedure ctl
  725.     -------------
  726.     
  727.     The canonical Control routine.  The only events we're interested
  728.     in are menu clicks; if we get one, call “clickmenu”.
  729.     
  730. *******************************************************************)
  731.         
  732.     procedure ctl(var device : DCtlEntry; var block : ParamBlockRec);
  733.  
  734.         var
  735.             globals            :    wpointer;
  736.         
  737.         begin
  738.             
  739.             if (device.dCtlMenu <> 0) and (block.csCode = accMenu) then begin
  740.                     globals := wpointer(device.dCtlStorage);
  741.                     clickmenu(block.csParam[1], globals);
  742.                 end;
  743.         
  744.         end;
  745.     
  746. (******************************************************************)
  747.             
  748.     end.
  749.     
  750. (******************************************************************)
  751.